package com.jmyd.txlcn.exception;
import com.jmyd.txlcn.base.vo.Result;
import com.jmyd.txlcn.enums.SystemErrorEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MultipartException;

import javax.annotation.Resource;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 处理Controller层全局异常处理
 *
 * @author samphin
 * @date 2020-12-1 14:13:46
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionController {

    @Resource
    private MultipartProperties multipartProperties;

    /**
     * 运行异常错误码
     */
    private static final int RUN_TIME_ERROR = 400;

    /**
     * 参数绑定出错
     */
    private static final int BIND_PARAM_ERROR = 300;

    /**
     * 数据校验错误码
     */
    private static final int VALIDATE_ERROR = 301;

    /**
     * SQL错误码
     */
    private static final int SQL_ERROR = 280;

    /**
     * 捕获自定义异常
     */
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<Result<Object>> handleCustomException(CustomException ex) {
        String errmsg = ex.getErrorMsg();
        if (StringUtils.isEmpty(errmsg)) {
            errmsg = ex.getMessage();
        }
        log.error("哎呦！异常::" + ex.getLocalizedMessage(), ex);
        return new ResponseEntity<>(Result.failure(ex.getSuccess(), ex.getErrorCode(), errmsg), HttpStatus.OK);
    }

    /**
     * 捕获参数绑定异常
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<Result<Object>> handleValidateException(BindException ex, BindingResult results) {
        StringBuilder sb = new StringBuilder();
        if (results.hasErrors()) {
            List<FieldError> errors = results.getFieldErrors();
            errors.forEach(x -> sb.append(x.getField()).append("=[").append(x.getDefaultMessage()).append("];"));
        }
        return new ResponseEntity<>(Result.failure(BIND_PARAM_ERROR, sb.toString()), HttpStatus.OK);
    }

    /**
     * 参数校验拦截器
     * 针对 @RequestBody参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Result<Object>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String errorMsg = e.getBindingResult().getAllErrors()
                .stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining(","));
        return new ResponseEntity<>(Result.failure(VALIDATE_ERROR, errorMsg), HttpStatus.OK);
    }

    /**
     * 捕获运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Result<Object>> handleRuntimeException(RuntimeException ex) {
        log.error(ex.getMessage(), ex);
        return new ResponseEntity<>(Result.failure(RUN_TIME_ERROR, ex.getMessage()), HttpStatus.OK);
    }

    /**
     * 捕获SQL执行异常
     */
    @ExceptionHandler(SQLException.class)
    public ResponseEntity<Result<Object>> handleSQLException(SQLException ex) {
        log.error(ex.getMessage(), ex);
        return new ResponseEntity<>(Result.failure(SQL_ERROR, ex.getMessage()), HttpStatus.OK);
    }

    /**
     * 处理文件上传大小超限制
     *
     * @param exception
     */
    @ExceptionHandler(value = MultipartException.class)
    public ResponseEntity<Result<Object>> fileUploadExceptionHandler(MultipartException exception) {
        String message = "上传文件大小超过限制[" + this.multipartProperties.getMaxRequestSize() + "]";
        log.error("哎呦！全局异常::" + message);
        return new ResponseEntity<>(Result.failure(VALIDATE_ERROR, message), HttpStatus.OK);
    }

    /**
     * 捕获全局异常
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Result<Object>> handleAllException(Exception e) {
        log.error("哎呦！全局异常::" + e.getLocalizedMessage(), e);
        return new ResponseEntity<>(Result.failure(SystemErrorEnum.PLEASE_RETRY_AGAIN), HttpStatus.OK);
    }
}
